package EcoSpeed;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;


/**
 * Class Statistics
 * Classe de statistique d'utilisation des assenseurs.
 */
public class Statistics {
    /**
     * Tableau de stats : [Jour de semaine][Heure][Etage]
     * => (nombre de demande ou poid) pour un jour de la semaine, une heure de la journée et un étage source
     */
    private int[][][] statistics;
    /**
     * Tableau de stats : [Etage]
     * => (nombre de demande ou poid) pour un étage source pour le moment actuel
     */
    private int[] currentStatistics;
    /**
     * Heure actuel
     */
    private int currentHour = 0;
    /**
     * Jour actuel
     */
    private int currentDay = Calendar.SATURDAY;
    /**
     * Constructeur
     */
    public Statistics () {
        currentStatistics = new int[Utils.NB_FLOORS];
        statistics = new int[7][24][Utils.NB_FLOORS];
        for (int i=0; i<7; i++) {
            for (int j=0; j<24; j++) {
                for (int k=0; k<Utils.NB_FLOORS; k++) {
                    statistics[i][j][k] = 1;
		    currentStatistics[k] = 1;
                }
            }
        }
    };
    /**
     * Set the value of statistics
     * Tableau de stats : [Jour de semaine][Heure][Etage]
     * => (nombre de demande ou poid) pour un jour de la semaine, une heure de la
     * journée et un étage source
     * @param newVar the new value of statistics
     */
    public void setStatistics ( int[][][] newVar ) {
        statistics = newVar;
    }

    /**
     * Get the value of statistics
     * Tableau de stats : [Jour de semaine][Heure][Etage]
     * => (nombre de demande ou poid) pour un jour de la semaine, une heure de la
     * journée et un étage source
     * @return the value of statistics
     */
    public int[][][] getStatistics ( ) {
        return statistics;
    }

    /**
     * Set the value of currentStatistics
     * Tableau de stats : [Etage]
     * => (nombre de demande ou poid) pour un étage source pour le moment actuel
     * @param newVar the new value of currentStatistics
     */
    private void setCurrentStatistics ( int[] newVar ) {
        currentStatistics = newVar;
    }

    /**
     * Get the value of currentStatistics
     * Tableau de stats : [Etage]
     * => (nombre de demande ou poid) pour un étage source pour le moment actuel
     * @return the value of currentStatistics
     */
    public int[] getCurrentStatistics ( ) {
        return currentStatistics;
    }

    /**
     * Set the value of currentHour
     * @param newVar the new value of currentHour
     */
    public void setCurrentHour ( int newVar ) {
        currentHour = newVar;

        // Si le nombre d'heure est arrivé à 24, on passe
        // au jour suivant.
        if (currentHour == 24) {
            currentHour = 0;
            currentDay = Utils.nextDay(currentDay);
        }
    }

    /**
     * Get the value of currentHour
     * @return the value of currentHour
     */
    public int getCurrentHour ( ) {
        return currentHour;
    }

    /**
     * Set the value of weekDay
     * Jour de la semain courant : 0 : Lundi , 1 Mardi , 2 Mercredi , 3 Jeudi , 4
     * Vendredi , 5 Samedi , 6 Dimanche
     * @param newVar the new value of weekDay
     */
    public void setWeekDay ( int newVar ) {
        currentDay = newVar;
    }

    /**
     * Get the value of weekDay
     * Jour de la semain courent : Monday, Tuesday, Wednesday, Thursday,
     * Friday, Saturday, Sunday.
     * @return the value of weekDay
     */
    public int getWeekDay ( ) {
        return currentDay;
    }

    /**
     * Fonction qui, en fonction de l'heure et du jour de la semain courrent, découpe
     * l'immeuble en n zone de même fréquentation.
     * @param        n Nombre d'ascenseur libre.
     * @return	     Tableau des n zones
     */
    public int[] nCile( int n ) {
        int currentTotal = 0;             //acumule le nombre total de demande
        int[] zones = new int[n];         //tableau pour le découpage en zones
        float sum;                          //somme pour acumuler les demande
        float avg;                          //moyenne des demande par ascenseur
        int j=0;
        /* estimation de la fréquentation totale pour ce jour dans les stat */
        for (int i = 0; i<Utils.NB_FLOORS; i++) {
            currentTotal += statistics[(currentDay-1)][currentHour][i];
        }
        avg = ((float)currentTotal/(float)n);
        sum = 0;
        j=0;
        /* Découpage en zones égales de fréquentation */
        for (int i=1; i<Utils.NB_FLOORS; i++) {
            sum += statistics[(currentDay-1)][currentHour][i];
            if (sum>=avg) {
                zones[j] = i;
                j++;
                sum -= avg;
                if (j==n) {
                    i = Utils.NB_FLOORS;
                }
            }
        }
        zones[n-1] = (Utils.NB_FLOORS-1);
        return zones;
    }


    /**
     * Fonction qui passe a l'heure suivante.
     *  - Effectue la moyenne entre les stats applicables et les données courantes
     *  - Réinitialise les données courantes
     *  - Passe a l'heure suivante
     */
    public void newHour(  ) {
        /* Définition de la moyenne */
        for (int i=0; i<Utils.NB_FLOORS; i++) {

            // On calcule la moyenne entre les stats enregistrées à cette heure et les stats moyennes.
            // On pondère les stats moyennes par un facteur 3 pour leur donner une plus grande importance.
            statistics[(currentDay-1)][currentHour][i] =  (3 * statistics[(currentDay-1)][currentHour][i] + currentStatistics[i]) / 4;
        }

        // Mise à jour des données courantes.
        this.clearCurrent();

        /*  Passage à l'heure suivante */
        this.setCurrentHour(this.currentHour+1);


    }

    /**
     * Purge les stats courent
     */
    public void clearCurrent(){
	    currentStatistics = new int[Utils.NB_FLOORS];
	    for (int k=0; k<Utils.NB_FLOORS; k++) {
		    currentStatistics[k] = 1;
	    }
    }

    /**
     * Modifie la date Actuel
     * @param d nouvelle date
     */
    public void setDate(Date d){
	    Calendar cal = GregorianCalendar.getInstance();
	    cal.setTime(d);
	    this.clearCurrent();
	    this.currentHour = cal.get(Calendar.HOUR_OF_DAY);
	    this.currentDay = cal.get(Calendar.DAY_OF_WEEK);
    }


    /**
     * Prise en compte du demande d'ascensceur pour les statistiques
     * @param        level Etage où a été effectue la demande
     */
    public void newRequest( int level ) {
        this.currentStatistics[level]++;
    }
    /**
    * Donne l'étage optimum de placement pour une zone de @startFloor à @endFloor
    * @return       int
    * @param        startFloor Etage de début de la zone
    * @param        endFloor Etage de fin de la zone
    */
    public int floorForZone( int startFloor, int endFloor ) {
        // Si les paramètres d'entrés ne sont pas valides.
        if (startFloor > endFloor) {
            return -1;
        }
	else {
            int totalRequest = 0, totalSum = 0, optimumFloor = startFloor;

            /*  calcul du nombre total de demandes entre ces étages */
            for (int i = startFloor ; i< endFloor; i++){
                totalRequest += statistics[(currentDay-1)][currentHour][i];
	    }
            /* On veut se placer au 3/4 de chaque zone  */
            totalRequest = (int) Math.round(Utils.RELOCATION_WEIGHT * totalRequest );

            /* Sauvegarde de l'étage optimum  */
	    if(totalRequest != 0){
		    for (int i = startFloor ; i<= endFloor; i++) {
			totalSum += statistics[(currentDay-1)][currentHour][i];
			if ( totalSum > totalRequest) {
			    optimumFloor = i;
			    break;
			}
		    }
	    }
	    else{
		    optimumFloor = startFloor + ((int) ((endFloor - startFloor) * Utils.RELOCATION_WEIGHT));
	    }
            return optimumFloor;
        }
    }
}

